#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <arpa/inet.h>

#include <vector>
using namespace std;

struct CONF
{
	int localfd;
	vector<int> workerArry;
	int nIndex;
}g_conf;

int sendfd(int fd, void *ptr, int nbytes, int sendfd)
{
	struct msghdr msg;
	struct iovec iov[1];

	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	iov[0].iov_base = ptr;
	iov[0].iov_len = nbytes;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

 	union
	{
		struct cmsghdr cm;
		char control[CMSG_SPACE(sizeof(int))];
	}control_un;
	
	struct cmsghdr *cmptr;

	msg.msg_control = control_un.control;
	msg.msg_controllen = sizeof(control_un.control);
	
	cmptr = CMSG_FIRSTHDR(&msg);
	cmptr->cmsg_len = CMSG_LEN(sizeof(int));
	cmptr->cmsg_level = SOL_SOCKET;
	cmptr->cmsg_type = SCM_RIGHTS;
	*((int *)CMSG_DATA(cmptr)) = sendfd;

	return (sendmsg(fd,&msg, 0));
};

//wait work process registe,after registe
//begin start networking
int waitregister(int nWorker)
{
	char szLocal[]="/tmp/unix_local";
	g_conf.localfd = socket(AF_LOCAL, SOCK_STREAM, 0);
	if(g_conf.localfd == -1)
	{
		perror("local socket");

		return -1;
	}

	unlink(szLocal);
	struct sockaddr_un addr;
	addr.sun_family = AF_LOCAL;
	strcpy(addr.sun_path, szLocal);
	int nrst = bind(g_conf.localfd, (struct sockaddr*)&addr, sizeof(addr));
	if(-1 == nrst)
	{
		perror("local bind");
		printf("local bind errno %d, %s\n", errno, strerror(errno));
		return -1;
	}

	nrst = listen(g_conf.localfd, 10);
	if(-1 == nrst)
	{
		perror("local listen");
		printf("local listen errno %d, %s\n", errno, strerror(errno));
		return -1;
	}
	
	while(g_conf.workerArry.size() < nWorker)
	{
		int workerfd = accept(g_conf.localfd, NULL,NULL);
		if(workerfd < 0)
		{
			if(errno == EINTR)
				continue;
			else
			{
				perror("local accept");
				return -1;
			}				
		}

		printf("recv a local connection!\n");
		g_conf.workerArry.push_back(workerfd);
	}

	g_conf.nIndex = 0;

	return 0;
};

int main()
{
	char szRecv[1024] = {0};

	waitregister(1);
	
	int listen_fd = socket(AF_INET,  SOCK_STREAM, 0);
	if(listen_fd <= 0)
	{
		perror("create socket");
		return -1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(10086);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	int rst = bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr));
	if(0 != rst)
	{
		perror("socket bind");
		printf("%d \t %s\n", errno, strerror(errno));
		return -1;
	}

	rst = listen(listen_fd, 20);
	if(0 != rst)
	{
		perror("socket listen");
		return -1;
	}

	printf("begin socket work!\n");
	for( ; ;)
	{
		int connfd = accept(listen_fd, NULL, NULL);
		if(connfd < 0)
		{
			if(errno == EINTR)
				continue;
			else
			{
				perror("socket accept");
				return -1;
			}
		}
		printf("recv a connection from client!fd %d\n", connfd);
		int nidx = (++g_conf.nIndex) % (g_conf.workerArry.size());
		int rst = sendfd(g_conf.workerArry[nidx], (void *)"", 1, connfd);
		if(rst == -1)
		{
			printf("send msg error: %d: %s", errno, strerror(errno));
		}
	}

	return 0;
}
